//
// Created by martin on 8/3/21.
//

#include <errno.h>		/* for definition of errno */
#include <stdarg.h>		/* ANSI C header file */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "error.h"

static void	err_doit(int, int, const char *, va_list);

/*
 * 系统调用相关的非致命错误
 * 打印消息并且返回
 */
void err_ret(const char *fmt, ...)
{
    va_list		ap; // typedef char *va_list;

    va_start(ap, fmt); // vs_start 获取可变参数列表的第一个参数的地址. 初始化va_list变量ap
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    return;
}

/*
 * 系统调用相关的致命错误
 * 打印消息并且终止程序
 */
void err_sys(const char *fmt, ...)
{
    va_list		ap;

    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    exit(1);
}

/**
 * 不与系统调用相关的非致命错误
 * 错误码通过显式参数error传递
 * 打印消息并返回
 */
void err_cont(int error, const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    err_doit(1, error, fmt, ap);
    va_end(ap);
}

/**
 * 不与系统调用相关的致命错误
 * 错误码通过显式参数error传递
 * 打印消息并终止程序
 */
void err_exit(int error, const char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    err_doit(1, error, fmt, ap);
    va_end(ap);
    exit(1);
}

/*
 * 系统调用相关的致命错误
 * 打印消息, 核心转储, 并且终止程序
 */
void err_dump(const char *fmt, ...)
{
    va_list		ap;

    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    abort();		    /* dump core and terminate */
    exit(1);		/* shouldn't get here */
}

/*
 * 不与系统调用相关的非致命错误
 * 打印消息, 并且返回
 */
void err_msg(const char *fmt, ...)
{
    va_list		ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
}

/*
 * 不与系统调用相关的致命错误
 * 打印消息, 并且终止程序
 */
void err_quit(const char *fmt, ...)
{
    va_list		ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}

/**
 * 打印消息, 并且返回调用者
 * 调用者指定参数errnoflag
 * @param errnoflag 错误标识, 值非0时, 才打印错误号转化成的错误详细信息. 值为0时, 不打印错误号对应错误详细信息.
 * @param error 错误号. 系统调用相关错误, 不用传错误号, 直接用errno作为错误号; 非系统调用错误(不设置errno的), 手动传入错误号
 * @param fmt 格式化字符串
 * @param ap 变参列表, fmt中有多少个转义字符, ap就需要提供同样个数参数值
 */
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
    int		errno_save;
    char	buf[MAXLINE];

    errno_save = error;		/* value caller might want printed */
    vsprintf(buf, fmt, ap); /* format string fmt write out to buf */
    if (errnoflag)
        sprintf(buf+strlen(buf), ": %s", strerror(errno_save)); /* append strerror(error) to buf */
    strcat(buf, "\n"); /* append "\n" to buf */
    fflush(stdout);		/* in case stdout and stderr are the same */
    fputs(buf, stderr); /* write out buf to stderr */
    fflush(stderr);		/* SunOS 4.1.* doesn't grok NULL argument */
}